// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// PLEASE READ BEFORE CHANGING THIS FILE!
//
// This file implements the support code for the out of bounds trap handler.
// Nothing in here actually runs in the trap handler, but the code here
// manipulates data structures used by the trap handler so we still need to be
// careful. In order to minimize this risk, here are some rules to follow.
//
// 1. Avoid introducing new external dependencies. The files in src/trap-handler
//    should be as self-contained as possible to make it easy to audit the code.
//
// 2. Any changes must be reviewed by someone from the crash reporting
//    or security team. See OWNERS for suggested reviewers.
//
// For more information, see https://goo.gl/yMeyUY.
//
// For the code that runs in the trap handler itself, see handler-inside.cc.

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <atomic>
#include <limits>
#include <windows.h>

#include "src/trap-handler/trap-handler-internal.h"
#include "src/trap-handler/trap-handler.h"
#include "src/base/thread-local.h"

namespace {
size_t gNextCodeObject = 0;

#ifdef ENABLE_SLOW_DCHECKS
constexpr bool kEnableSlowChecks = true;
#else
constexpr bool kEnableSlowChecks = false;
#endif
}

namespace v8 {
namespace internal {
    namespace trap_handler {

        base::ThreadLocalPointer<int>* g_thread_in_wasm_code_tls = NULL;

        constexpr size_t kInitialCodeObjectSize = 1024;
        constexpr size_t kCodeObjectGrowthFactor = 2;

        constexpr size_t HandlerDataSize(size_t num_protected_instructions)
        {
            return offsetof(CodeProtectionInfo, instructions) + num_protected_instructions * sizeof(ProtectedInstructionData);
        }

        namespace {
#ifdef DEBUG
            bool IsDisjoint(const CodeProtectionInfo* a, const CodeProtectionInfo* b)
            {
                if (a == nullptr || b == nullptr) {
                    return true;
                }
                return a->base >= b->base + b->size || b->base >= a->base + a->size;
            }
#endif

            // Verify that the code range does not overlap any that have already been
            // registered.
            void VerifyCodeRangeIsDisjoint(const CodeProtectionInfo* code_info)
            {
                for (size_t i = 0; i < gNumCodeObjects; ++i) {
                    DCHECK(IsDisjoint(code_info, gCodeObjects[i].code_info));
                }
            }

            void ValidateCodeObjects()
            {
                // Sanity-check the code objects
                for (unsigned i = 0; i < gNumCodeObjects; ++i) {
                    const auto* data = gCodeObjects[i].code_info;

                    if (data == nullptr)
                        continue;

                    // Do some sanity checks on the protected instruction data
                    for (unsigned i = 0; i < data->num_protected_instructions; ++i) {
                        DCHECK_GE(data->instructions[i].instr_offset, 0);
                        DCHECK_LT(data->instructions[i].instr_offset, data->size);
                        DCHECK_GE(data->instructions[i].landing_offset, 0);
                        DCHECK_LT(data->instructions[i].landing_offset, data->size);
                        DCHECK_GT(data->instructions[i].landing_offset,
                            data->instructions[i].instr_offset);
                    }
                }

                // Check the validity of the free list.
                size_t free_count = 0;
                for (size_t i = gNextCodeObject; i != gNumCodeObjects;
                     i = gCodeObjects[i].next_free) {
                    DCHECK_LT(i, gNumCodeObjects);
                    ++free_count;
                    // This check will fail if we encounter a cycle.
                    DCHECK_LE(free_count, gNumCodeObjects);
                }

                // Check that all free entries are reachable via the free list.
                size_t free_count2 = 0;
                for (size_t i = 0; i < gNumCodeObjects; ++i) {
                    if (gCodeObjects[i].code_info == nullptr) {
                        ++free_count2;
                    }
                }
                DCHECK_EQ(free_count, free_count2);
            }
        } // namespace

        CodeProtectionInfo* CreateHandlerData(
            Address base, size_t size, size_t num_protected_instructions,
            const ProtectedInstructionData* protected_instructions)
        {
            const size_t alloc_size = HandlerDataSize(num_protected_instructions);
            CodeProtectionInfo* data = reinterpret_cast<CodeProtectionInfo*>(malloc(alloc_size));

            if (data == nullptr) {
                return nullptr;
            }

            data->base = base;
            data->size = size;
            data->num_protected_instructions = num_protected_instructions;

            memcpy(data->instructions, protected_instructions,
                num_protected_instructions * sizeof(ProtectedInstructionData));

            return data;
        }

        int RegisterHandlerData(
            Address base, size_t size, size_t num_protected_instructions,
            const ProtectedInstructionData* protected_instructions)
        {

            CodeProtectionInfo* data = CreateHandlerData(
                base, size, num_protected_instructions, protected_instructions);

            if (data == nullptr) {
                abort();
            }

            MetadataLock lock;

            if (kEnableSlowChecks) {
                VerifyCodeRangeIsDisjoint(data);
            }

            size_t i = gNextCodeObject;

            // Explicitly convert std::numeric_limits<int>::max() to unsigned to avoid
            // compiler warnings about signed/unsigned comparisons. We aren't worried
            // about sign extension because we know std::numeric_limits<int>::max() is
            // positive.
            const size_t int_max = std::numeric_limits<int>::max();

            // We didn't find an opening in the available space, so grow.
            if (i == gNumCodeObjects) {
                size_t new_size = gNumCodeObjects > 0
                    ? gNumCodeObjects * kCodeObjectGrowthFactor
                    : kInitialCodeObjectSize;

                // Because we must return an int, there is no point in allocating space for
                // more objects than can fit in an int.
                if (new_size > int_max) {
                    new_size = int_max;
                }
                if (new_size == gNumCodeObjects) {
                    free(data);
                    return kInvalidIndex;
                }

                // Now that we know our new size is valid, we can go ahead and realloc the
                // array.
                gCodeObjects = static_cast<CodeProtectionInfoListEntry*>(
                    realloc(gCodeObjects, sizeof(*gCodeObjects) * new_size));

                if (gCodeObjects == nullptr) {
                    abort();
                }

                memset(gCodeObjects + gNumCodeObjects, 0,
                    sizeof(*gCodeObjects) * (new_size - gNumCodeObjects));
                for (size_t j = gNumCodeObjects; j < new_size; ++j) {
                    gCodeObjects[j].next_free = j + 1;
                }
                gNumCodeObjects = new_size;
            }

            DCHECK(gCodeObjects[i].code_info == nullptr);

            // Find out where the next entry should go.
            gNextCodeObject = gCodeObjects[i].next_free;

            if (i <= int_max) {
                gCodeObjects[i].code_info = data;

                if (kEnableSlowChecks) {
                    ValidateCodeObjects();
                }

                return static_cast<int>(i);
            } else {
                free(data);
                return kInvalidIndex;
            }
        }

        void ReleaseHandlerData(int index)
        {
            if (index == kInvalidIndex) {
                return;
            }
            DCHECK_GE(index, 0);

            // Remove the data from the global list if it's there.
            CodeProtectionInfo* data = nullptr;
            {
                MetadataLock lock;

                data = gCodeObjects[index].code_info;
                gCodeObjects[index].code_info = nullptr;

                gCodeObjects[index].next_free = gNextCodeObject;
                gNextCodeObject = index;

                if (kEnableSlowChecks) {
                    ValidateCodeObjects();
                }
            }
            // TODO(eholk): on debug builds, ensure there are no more copies in
            // the list.
            DCHECK_NOT_NULL(data); // make sure we're releasing legitimate handler data.
            free(data);
        }

#ifdef SUPPORT_XP_CODE

        DISABLE_ASAN bool IsThreadInWasm()
        {
//             int* thread_in_wasm_code_ptr = (int*)::TlsGetValue(g_thread_in_wasm_code_tls);
            int* thread_in_wasm_code_ptr = g_thread_in_wasm_code_tls->Get();
            return *thread_in_wasm_code_ptr != 0;
        }

        void SetThreadInWasm()
        {
            if (IsTrapHandlerEnabled()) {
                DCHECK(!IsThreadInWasm());

                //int* thread_in_wasm_code_ptr = (int*)::TlsGetValue(g_thread_in_wasm_code_tls);
                int* thread_in_wasm_code_ptr = g_thread_in_wasm_code_tls->Get();
                *thread_in_wasm_code_ptr = 1;
            }
        }

        void ClearThreadInWasm()
        {
            if (IsTrapHandlerEnabled()) {
                DCHECK(IsThreadInWasm());

                //int* thread_in_wasm_code_ptr = (int*)::TlsGetValue(g_thread_in_wasm_code_tls);
                int* thread_in_wasm_code_ptr = g_thread_in_wasm_code_tls->Get();
                *thread_in_wasm_code_ptr = 0;
            }
        }

        int* GetThreadInWasmThreadLocalAddress()
        {
            int* thread_in_wasm_code_ptr = nullptr;
            if (0 == g_thread_in_wasm_code_tls) {
                //g_thread_in_wasm_code_tls = ::TlsAlloc();
                g_thread_in_wasm_code_tls = new base::ThreadLocalPointer<int>();

                thread_in_wasm_code_ptr = (int*)malloc(sizeof(int));
                *thread_in_wasm_code_ptr = 0;
                //::TlsSetValue(g_thread_in_wasm_code_tls, thread_in_wasm_code_ptr);
                g_thread_in_wasm_code_tls->Set(thread_in_wasm_code_ptr);
            } else {
                //thread_in_wasm_code_ptr = (int*)::TlsGetValue(g_thread_in_wasm_code_tls);
                thread_in_wasm_code_ptr = g_thread_in_wasm_code_tls->Get();
            }

            return thread_in_wasm_code_ptr;
        }

#else

        int* GetThreadInWasmThreadLocalAddress()
        {
            return &g_thread_in_wasm_code;
        }

#endif

        size_t GetRecoveredTrapCount()
        {
            return gRecoveredTrapCount.load(std::memory_order_relaxed);
        }

#if !V8_TRAP_HANDLER_SUPPORTED
        // This version is provided for systems that do not support trap handlers.
        // Otherwise, the correct one should be implemented in the appropriate
        // platform-specific handler-outside.cc.
        bool RegisterDefaultTrapHandler() { return false; }

        void RemoveTrapHandler() { }
#endif

        bool g_is_trap_handler_enabled;

        bool EnableTrapHandler(bool use_v8_handler)
        {
            if (!V8_TRAP_HANDLER_SUPPORTED) {
                return false;
            }
            if (use_v8_handler) {
                g_is_trap_handler_enabled = RegisterDefaultTrapHandler();
                return g_is_trap_handler_enabled;
            }
            g_is_trap_handler_enabled = true;
            return true;
        }

    } // namespace trap_handler
} // namespace internal
} // namespace v8
